package org.jeecg.modules.miniAppCustomerEndPoint.service;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.miniAppCustomerEndPoint.constant.JxCustomerConstant;
import org.jeecg.modules.miniAppCustomerEndPoint.entity.JxUserDo;
import org.jeecg.modules.miniAppCustomerEndPoint.entity.MiniAppSessoinEntity;
import org.jeecg.modules.miniAppCustomerEndPoint.mapper.JxUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Date;
import java.util.HashMap;

@Service
@Slf4j
public class JxUserServiceImpl {

    @Autowired
    private JxUserMapper jxUserMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Value("${wx.mp.app-id}")
    private String appId;

    @Value("${wx.mp.secret}")
    private String appSecret;

    /**
     *
     * @param code 微信用户的授权码，前端申请获取微信用户授权码时，会调用方法在微信客户端弹出一个弹窗，微信用户点击后才能从 微信服务上获取到授权码
     * @return
     * @throws WxErrorException
     */
    public HashMap<String, Object> loginForWx(String code) throws WxErrorException {
        if (StringUtils.isBlank(code)) {
            throw new JeecgBootException("code empty");
        }

        MiniAppSessoinEntity miniAppSessoinEntity =  code2Session(code);
        if (miniAppSessoinEntity == null || StringUtils.isEmpty(miniAppSessoinEntity.getOpenId())) {
            throw new JeecgBootException("获取用户信息失败");
        }
        // select * from hc_user where open_id = <openId> order by update_time desc limit 1;
        JxUserDo jxUserDo = jxUserMapper.selectOne(new LambdaQueryWrapper<JxUserDo>().eq(JxUserDo::getOpenId, miniAppSessoinEntity.getOpenId())
                .orderByDesc(JxUserDo::getUpdateTime).last(" limit 1 ")
        );
        // 若表中没有，表示用户首次登陆，向hc_user表中添加记录，保存主要的信息：微信用户的 openid，phone
        if (jxUserDo == null) {
            jxUserDo = new JxUserDo()
                    .setOpenId(miniAppSessoinEntity.getOpenId())
                    .setSiteId("unknown")
                    .setPickupAddressName("unknown")
                    .setPickupAddressCode("unknown")
                    .setPhone("")
                    .setHeadPortrait("http://btprint.yongshantuan.cn/static/images/user/user.png")
                    .setNickName("用户001")
                    .setNameTakingMeal("用户001")
                    .setCreateTime(new Date());
            jxUserMapper.insert(jxUserDo);
        }
        // 基于微信用户的openid，生成 jwt 作为该用户后期调用接口的 token，JWT字符串格式：eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2OTkwOTYzMjIsInVzZXJuYW1lIjoiYWRtaW4ifQ.XuuK8AbuTnfwCtmbPsT_k25GCqflHiUjKrqGFujrgjU
        // JWT字符串由3部分组成：header.payload.signature；使用 点号 进行分隔
        // 生成token，格式为 {"alg":"HMAC256","typ":"JWT"}.{"username":"hc_user_login_token_<openid>"}.HMAC256(xxx);
        // JwtUtil.sign() 方法中，将jwt token的有效期，设置成了固定的 30天；没有将 token的有效期的设置，暴露在 sign() 方法的形参中
        //
        // 注意：将用户的id 编码到token中，不将open_id 编码到token中；如果小程序端需要用到 open_id ，则通过其他接口返回
        String token = JwtUtil.sign(JxCustomerConstant.JX_USER_LOGIN_TOKEN + jxUserDo.getId(), JxCustomerConstant.JX_USER_LOGIN_PASSWORD);

        // 设置token缓存有效时间； 每个token有效期为 60 天
        redisUtil.set(JxCustomerConstant.JX_PREFIX_USER_TOKEN + jxUserDo.getId(), token);
        redisUtil.expire(JxCustomerConstant.JX_PREFIX_USER_TOKEN + jxUserDo.getId(), JwtUtil.EXPIRE_TIME * 2 / 1000);

        HashMap<String, Object> resultData = new HashMap<>();
        resultData.put("token", token);
        resultData.put("userId", jxUserDo.getId());
        return resultData;
    }

    /**
     * 根据用户临时授权code，获取openid 和 session_key （微信接口返回的数据都是加密的，需要使用此 session_key 进行解密）
     * @param code
     * @return
     */
    private MiniAppSessoinEntity code2Session(String code) {

        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + appSecret + "&js_code=" + code + "&grant_type=authorization_code";
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);

        if (responseEntity !=null && responseEntity.getStatusCode() == HttpStatus.OK) {
            String sessionData = responseEntity.getBody();
            MiniAppSessoinEntity sessoinEntity = JSONObject.parseObject(sessionData, MiniAppSessoinEntity.class);
            return sessoinEntity;
        } else {
            throw new JeecgBootException("code2session failed");
        }
    }

    public JSONObject getUserBasicInfo(String userId) {

        JSONObject formatedUserInfo = new JSONObject();
        JxUserDo userDo = jxUserMapper.selectById(userId);
        if (userDo != null) {
            formatedUserInfo.put("nickName", userDo.getNickName());
            formatedUserInfo.put("userId", JxCustomerConstant.USER_ID_PREFIX  + userDo.getId().substring(userDo.getId().length() - 8)); // 在小程序-我的页面，左上角，仅展示用户id的最后8位；
            formatedUserInfo.put("headPortrait", userDo.getHeadPortrait());
            // todo 用户钱包余额，优惠券个数，积分值
            formatedUserInfo.put("wallet", 8590);
            formatedUserInfo.put("coupons", 3);
            formatedUserInfo.put("points", 30);
        } else {
            formatedUserInfo.put("nickName", JxCustomerConstant.USER_DEFALUT_NICKNAME);
            formatedUserInfo.put("userId", JxCustomerConstant.USER_DEFALUT_ID); // 在小程序-我的页面，左上角，仅展示用户id的最后8位；
            formatedUserInfo.put("headPortrait", JxCustomerConstant.USER_DEFALUT_HEADPORTRAIT);
            // todo 用户钱包余额，优惠券个数，积分值
            formatedUserInfo.put("wallet", 0);
            formatedUserInfo.put("coupons", 0);
            formatedUserInfo.put("points", 0);
            log.error("无法查询到用户id：{} 的信息，返回默认值", userId);
        }

        return formatedUserInfo;
    }

}
